package de.riceman.simpleGo.view.views.visualizationView;

import java.util.Iterator;
import java.util.Observable;

/**
 * @author reismade
 *
 * @version $Revision$
 */
//@Service
public class ForceDirectedGraphLayouter implements ForceDirectedGraphLayouterInterface  {

	private ForceDirectedGraph graph;

	private Timer timer;
	private double factor;
	private double terminationCondition;
	private int panelSize;
	private int step;

	public ForceDirectedGraphLayouter(double factor, double terminationCondition, int panelSize, Timer timer, ForceDirectedGraph forceDirectedGraph){
		this.factor = factor;
		this.terminationCondition = terminationCondition;
		this.panelSize = panelSize;		
		this.timer = timer;
		this.graph = forceDirectedGraph;
	};
	
/*	public void show() {
		this.setChanged();
		this.notifyObservers();
	}*/
	
	public void run() {
		System.out.println("run()");
		Iterator<ForcedNode> outerIt;
		Iterator<ForcedNode> innerIt;
		Iterator<SpringEdge> edgeIt;
		
		ForcedNode currentNode, otherNode;
		SpringEdge currentEdge;
		
		Vector3D totalForce, moveVector;
		
		double moveX;
		double moveY;
		
		timer.start();
		
		step = 0;
		
		/*this.setChanged();
		this.notifyObservers();	*/
		
		do {
	
			totalForce = new Vector3D(0,0,0);
			
			//calculate forces on each node
			//calculate spring forces on each node

			outerIt = this.graph.getNodes().iterator();
			 
			while (outerIt.hasNext()) {
				currentNode = outerIt.next();
				 
				currentNode.setForce(new Vector3D(0,0,0));
				 
				edgeIt = this.graph.getEdges().iterator();
				 
				while (edgeIt.hasNext()) {
					currentEdge = edgeIt.next();
					Vector3D f;
					 
					if (currentEdge.getVertexA().getId() == currentNode.getId()) {
						f = currentEdge.getForceA();
						currentNode.applyForce(f);
					}
					if (currentEdge.getVertexB().getId() == currentNode.getId()) {
						f = currentEdge.getForceB();
						currentNode.applyForce(f);
					}
					 
				}
			}
			 
			 				  
			//calculate the anti-gravitational forces on each node

			outerIt = this.graph.getNodes().iterator();
			
			while (outerIt.hasNext()) {
				currentNode = outerIt.next();
				
				innerIt = this.graph.getNodes().iterator();
				
				while (innerIt.hasNext()) {
					
					otherNode = innerIt.next();
					
					
					if (currentNode.getId() != otherNode.getId()) {
						double dx = otherNode.getPosition().getX() - currentNode.getPosition().getX();
						double dy = otherNode.getPosition().getY() - currentNode.getPosition().getY();
						double r = Math.sqrt(dx*dx + dy*dy);
						
						//F = G*m1*m2/r^2 
						if (r != 0) {
							double f = factor*(currentNode.getMass()*otherNode.getMass()/(r*r));
							Vector3D vf = new Vector3D(-dx*f, -dy*f, 0);
							currentNode.applyForce(vf);
						}
					}
				}
			}
			 
			//move nodes according to forces
			outerIt = this.graph.getNodes().iterator();
			 
			while (outerIt.hasNext()) {
				currentNode = outerIt.next();

				totalForce = totalForce.add(new Vector3D(Math.abs(currentNode.getForce().getX()), Math.abs(currentNode.getForce().getY()), 0));
				currentNode.setPosition(currentNode.getPosition().add(currentNode.getForce()));

			}
			
			//System.out.println(totalForce);
			
			//move graph to the center
			moveX = (panelSize/2.0) - this.graph.getCentroid().getX();
			moveY = (panelSize/2.0) - this.graph.getCentroid().getY();
			
			moveVector = new Vector3D(moveX, moveY, 0);
			
			outerIt = this.graph.getNodes().iterator();
			 
			while (outerIt.hasNext()) {
				
				currentNode = outerIt.next();
				currentNode.setPosition(currentNode.getPosition().add(moveVector));
			}
			
			System.out.println(step);
			step++;
			
			/*if (step % viewUpdateSteps == 0) {
				this.setChanged();
				this.notifyObservers();				
			}*/
			
		} while (totalForce.getX() > terminationCondition || totalForce.getY() > terminationCondition);
		
/*		this.setChanged();
		this.notifyObservers();*/
		
	}
	
	public ForceDirectedGraph getGraph() {
		
		return graph;
	}
	
	public void setTimer(Timer timer) {
	
		this.timer = timer;
	}

	public Timer getTimer() {
	
		return timer;
	}
	
	public void setGraph(ForceDirectedGraph graph) {
		
		this.graph = graph;
	}

	
	public void setFactor(double factor) {
	
		this.factor = factor;
	}

	
	public void setTerminationCondition(double terminationCondition) {
	
		this.terminationCondition = terminationCondition;
	}

	
	public void setPanelSize(int panelSize) {
	
		this.panelSize = panelSize;
	}
	
	public int getStep() {
		return step;
	}
}
